﻿using ArtMath.Attribute;
using ArtMath.Component.Point;
using ArtMath.Converter;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Data;
using ArtMath.Helper;
using ArtMath.PointCollection;
using ArtMath.Resource;
using ArtMath.Setting;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using ArtMath.CommonType;

namespace ArtMath.Component.ConicalSection
{
    /// <summary>
    /// 双曲线基类
    /// </summary>
    [Serializable]
    public abstract class HyperbolaBase : LineBase, IConic
    {
        double step;
        //protected double focusAngle;
        //protected PointD center, Pcenter;
        protected Hyperbola ske = new Hyperbola() { Curves = new LengthCurve[] { new LengthCurve(), new LengthCurve() } },
            pske = new Hyperbola() { Curves = new Curve[] { new Curve(), new Curve() } };
        protected LengthCurve newCurL = new LengthCurve(), newCurR = new LengthCurve();
        protected Curve newPCurL = new Curve(), newPCurR = new Curve();
        List<PointF[]> track = new List<PointF[]>();
        PadStringX prec;
        PropertyDisplayer cDisplayer, aDisplayer, bDisplayer, fp1Displayer, fp2Displayer, eDisplayer, ctDisplayer, pDisplayer;
        public HyperbolaBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            Accuracy = new PadStringX(Pad, Settings.Instance.HyperbolaAccuracy);
            step = 1 / Settings.Instance.HyperbolaAccuracy;
            FocalHalfLengthDisplayer = new PropertyDisplayer(this, "FocalHalfLength");
            RealAxisHalfLengthDisplayer = new PropertyDisplayer(this, "RealAxisHalfLength");
            ImaginaryAxisHalfLengthDisplayer = new PropertyDisplayer(this, "ImaginaryAxisHalfLength");
            FocusPoint1Displayer = new PropertyDisplayer(this, "Focus1") { OffSet = AnchorOffset };
            FocusPoint2Displayer = new PropertyDisplayer(this, "Focus2") { OffSet = AnchorOffset };
            EccentricityDisplayer = new PropertyDisplayer(this, "Eccentricity");
            CenterDisplayer = new PropertyDisplayer(this, "Center");
            FocusDirectrixDistanceDisplayer = new PropertyDisplayer(this, "FocusDirectrixDistance");
        }
        public override PointD MassPoint
        {
            get { return Center; }
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                return new PointCollectionsManager(ske);
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                return new PointCollectionsManager(pske);
            }
        }
        public override PointCollectionsManager NewSkeleton
        {
            get
            {
                build(newCurL, null, newCurR, null, NewRealAxisHalfLength, NewImaginaryAxisHalfLength, NewFocusAngle, NewCenter);
                return new PointCollectionsManager(new Hyperbola(NewCenter, NewFocusAngle, NewRealAxisHalfLength, NewImaginaryAxisHalfLength) { Curves = new LengthCurve[] { newCurL, newCurR } });
            }
        }
        public override PointCollectionsManager NewPSkeleton
        {
            get
            {
                build(null, newPCurL, null, newPCurR, NewRealAxisHalfLength, NewImaginaryAxisHalfLength, NewFocusAngle, NewCenter);
                return new PointCollectionsManager(new Hyperbola() { Center = NewPCenter, Curves = new Curve[] { newPCurL, newPCurR } });
            }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("RealAxisHalfLength"), ReadOnly(true)]
        public double RealAxisHalfLength
        {
            get { return ske.RealAxisHalfLength; }
            protected set
            {
                ske.RealAxisHalfLength = value;
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual double NewRealAxisHalfLength
        {
            get { return RealAxisHalfLength; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("ImaginaryAxisHalfLength"), ReadOnly(true)]
        public double ImaginaryAxisHalfLength
        {
            get { return ske.ImaginaryAxisHalfLength; }
            protected set
            {
                ske.ImaginaryAxisHalfLength = value;
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual double NewImaginaryAxisHalfLength
        {
            get { return ImaginaryAxisHalfLength; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("FocalHalfLength")]
        public virtual double FocalHalfLength
        {
            get { return Math.Sqrt(RealAxisHalfLength * RealAxisHalfLength + ImaginaryAxisHalfLength * ImaginaryAxisHalfLength); }
        }
        [Browsable(false)]
        public virtual double NewFocalHalfLength
        {
            get { return Math.Sqrt(NewRealAxisHalfLength * NewRealAxisHalfLength + NewImaginaryAxisHalfLength * NewImaginaryAxisHalfLength); }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("Eccentricity")]
        public double Eccentricity
        {
            get { return FocalHalfLength / RealAxisHalfLength; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName(null, "Hyperbola", "Center"), TypeConverter(typeof(IntelliPointConverter))]
        public PointD Center
        {
            get { return ske.Center; }
        }

        [Browsable(false)]
        public PointD PCenter
        {
            get { return pske.Center; }
        }

        [Browsable(false)]
        public virtual PointD NewCenter
        {
            get { return Center; }
        }

        [Browsable(false)]
        public virtual PointD NewPCenter
        {
            get { return PCenter; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("{0}1", "Focus"), TypeConverter(typeof(IntelliPointConverter))]
        public virtual PointD Focus1
        {
            get { return Center.Offset(FocusAngle + Math.PI, FocalHalfLength); }
        }
        [Browsable(false)]
        public virtual PointD NewFocus1
        {
            get { return NewCenter.Offset(NewFocusAngle + Math.PI, NewFocalHalfLength); }
        }
        [Browsable(false)]
        public virtual PointD PFocus1
        {
            get { return Pad.XYToPxy(Focus1); }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("{0}2", "Focus"), TypeConverter(typeof(IntelliPointConverter))]
        public virtual PointD Focus2
        {
            get { return Center.Offset(FocusAngle, FocalHalfLength); }
        }
        [Browsable(false)]
        public virtual PointD NewFocus2
        {
            get { return NewCenter.Offset(NewFocusAngle, NewFocalHalfLength); }
        }
        [Browsable(false)]
        public virtual PointD PFocus2
        {
            get { return Pad.XYToPxy(Focus2); }
        }
        [MultilingualCategory("Structure"), DefaultValue(typeof(PadStringX), "1000"), MultilingualDisplayName("Accuracy")]
        public PadStringX Accuracy
        {
            get { return prec; }
            set
            {
                if (prec is object)
                {
                    prec.ParametersValueChanged -= Accuracy_ParametersValueChanged;
                    prec.ExpressionSet -= Accuracy_ExpressionSet;
                }
                prec = value;
                if (prec is object)
                {
                    prec.ParametersValueChanged += Accuracy_ParametersValueChanged;
                    prec.ExpressionSet += Accuracy_ExpressionSet;
                }
            }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("DirectedAngle"), TypeConverter(typeof(IntelliAngleConverter)), ReadOnly(true)]
        public double FocusAngle
        {
            get { return ske.RotateAngle; }
            protected set
            {
                ske.RotateAngle = value;
                Pad.Canvas.Invalidate();
            }
        }
        [Browsable(false)]
        public virtual double NewFocusAngle
        {
            get { return FocusAngle; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "FocusDirectrixDistance", "Displayer")]
        public PropertyDisplayer FocusDirectrixDistanceDisplayer
        {
            get { return pDisplayer; }
            set { pDisplayer = value; }
        }
        //[Browsable(false)]
        //public PaintableCurve[] Curves
        //{
        //    get { return new PaintableCurve[] { cur1, cur2 }; }
        //}
        //[Browsable(false)]
        //public PaintableCurve[] NewCurves
        //{
        //    get
        //    {
        //        build(newCur1, newCur2, NewRealAxisHalfLength, NewImaginaryAxisHalfLength, NewFocusAngle, NewCenter);
        //        return new PaintableCurve[] { newCur1, newCur2 };
        //    }
        //}
        public override PointD AnchorPoint(PropertyDisplayer disp)
        {
            if (disp == FocusPoint1Displayer)
            {
                return PFocus1;
            }
            if (disp == FocusPoint2Displayer)
            {
                return PFocus2;
            }
            return PCenter;
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "FocalHalfLength", "Displayer")]
        public PropertyDisplayer FocalHalfLengthDisplayer
        {
            get { return cDisplayer; }
            set { cDisplayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "RealAxisHalfLength", "Displayer")]
        public PropertyDisplayer RealAxisHalfLengthDisplayer
        {
            get { return aDisplayer; }
            set { aDisplayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "ImaginaryAxisHalfLength", "Displayer")]
        public PropertyDisplayer ImaginaryAxisHalfLengthDisplayer
        {
            get { return bDisplayer; }
            set { bDisplayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName("{0}1+{1}", "Focus", "Displayer")]
        public PropertyDisplayer FocusPoint1Displayer
        {
            get { return fp1Displayer; }
            set { fp1Displayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName("{0}2+{1}", "Focus", "Displayer")]
        public PropertyDisplayer FocusPoint2Displayer
        {
            get { return fp2Displayer; }
            set { fp2Displayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "Eccentricity", "Displayer")]
        public PropertyDisplayer EccentricityDisplayer
        {
            get { return eDisplayer; }
            set { eDisplayer = value; }
        }
        [MultilingualCategory(null, "Sketchpad", "Display"), MultilingualDisplayName(null, "Hyperbola", "Center", "Displayer")]
        public PropertyDisplayer CenterDisplayer
        {
            get { return ctDisplayer; }
            set { ctDisplayer = value; }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("FocusDirectrixDistance")]
        public double FocusDirectrixDistance
        {
            get
            {
                return ImaginaryAxisHalfLength * ImaginaryAxisHalfLength / FocalHalfLength;
            }
        }
        [Browsable(false)]
        public double NewFocusDirectrixDistance
        {
            get
            {
                return NewImaginaryAxisHalfLength * NewImaginaryAxisHalfLength / NewFocalHalfLength;
            }
        }
        [Browsable(false)]
        public double NewEccentricity
        {
            get { return NewFocalHalfLength / NewRealAxisHalfLength; }
        }
        void Accuracy_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            if (prec.Value > 0)
            {
                step = 1 / prec.Value;
                BuildSamples();
            }
        }
        void Accuracy_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (prec.Value > 0)
            {
                step = 1 / prec.Value;
                BuildSamples();
            }
            else
            {
                Pad.ShowMessage(Resources.JoinLocalizedString(string.Format(Resources.Translate("Of"), Resources.Translate("Hyperbola"), Resources.Translate("Accuracy")), Resources.Translate("MustPositive")), MessageType.Error);
                prec.SetExpression(e.OldExpression, false);
            }
        }
        public override void ComponentBuilding(Base component)
        {
            BuildSamples();
            ShowStatus($"{Resources.Translate("Creating")}{Resources.ColonSeparate}{Name}{Resources.LeftParenthesis}{Resources.JoinLocalizedString("Hyperbola", "Center")}{Resources.ColonSeparate}{Pad.ShowPoint(Center)}{Resources.CommaSeparate}{Resources.Translate("RealAxisHalfLength")} = {RealAxisHalfLength}{Resources.CommaSeparate}{Resources.Translate("ImaginaryAxisHalfLength")} = {ImaginaryAxisHalfLength}{Resources.CommaSeparate}{Resources.Translate("FocalHalfLength")} = {FocalHalfLength}{Resources.CommaSeparate}{Resources.Translate("Eccentricity")} = {Eccentricity}）");
            base.ComponentBuilding(component);
        }
        public override void ComponentChanged(ShapeBase component)
        {
            RealAxisHalfLength = NewRealAxisHalfLength;
            ImaginaryAxisHalfLength = NewImaginaryAxisHalfLength;
            ske.Center = NewCenter;
            pske.Center = NewPCenter;
            FocusAngle = NewFocusAngle;
            BuildSamples();
            base.ComponentChanged(component);
        }
        public override void CheckInRegion(RectangleD rect)
        {
            SetInPicker(rect.IntersectCurves(pske.Curves));
            base.CheckInRegion(rect);
        }
        public override void RefreshPropertyDisplayersText()
        {
            FocalHalfLengthDisplayer.RefreshText();
            RealAxisHalfLengthDisplayer.RefreshText();
            ImaginaryAxisHalfLengthDisplayer.RefreshText();
            FocusPoint1Displayer.RefreshText();
            FocusPoint2Displayer.RefreshText();
            EccentricityDisplayer.RefreshText();
            CenterDisplayer.RefreshText();
            base.RefreshPropertyDisplayersText();
        }
        public override void RefreshPropertyDisplayersLocation()
        {
            FocalHalfLengthDisplayer.RefreshLocation();
            RealAxisHalfLengthDisplayer.RefreshLocation();
            ImaginaryAxisHalfLengthDisplayer.RefreshLocation();
            FocusPoint1Displayer.RefreshLocation();
            FocusPoint2Displayer.RefreshLocation();
            EccentricityDisplayer.RefreshLocation();
            CenterDisplayer.RefreshLocation();
            base.RefreshPropertyDisplayersLocation();
        }
        public override void PaintProperties(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible)
            {
                FocalHalfLengthDisplayer.Paint(gra);
                RealAxisHalfLengthDisplayer.Paint(gra);
                ImaginaryAxisHalfLengthDisplayer.Paint(gra);
                FocusPoint1Displayer.Paint(gra);
                FocusPoint2Displayer.Paint(gra);
                EccentricityDisplayer.Paint(gra);
                CenterDisplayer.Paint(gra);
            }
            base.PaintProperties(gra);
        }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible && IsCreationCompleted)
            {
                foreach (Curve item in pske.Curves)
                {
                    item.Paint(gra, LinePen);
                }
            }
            base.Paint(gra);
        }
        //public override PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        //{
        //    return ske.SlideDestination(currentLocation, length, out reverse);//使用NewSkeleton可能会降低性能
        //}
        public override void AddTracks()
        {
            foreach (Curve item in pske.Curves)
            {
                track.Add(item.FSamples);
            }
        }
        public override void ClearTracks()
        {
            track.Clear();
            base.ClearTracks();
        }
        public override void PaintTrack(Graphics gra)
        {
            if (ShowTrack_)
            {
                for (int i = 0; i < track.Count; i++)
                {
                    gra.DrawCurve(TrackPen, track[i]);
                }
            }
            base.PaintTrack(gra);
        }
        public override void InternalPrepare(PointBase movePoint, ShapeBase sender = null)
        {
            foreach (PointBase item in OutChildren_Point)
            {
                PointD std = (item.ToPointD() - Center).Rotate(-FocusAngle);
                //double angle = 0;
                //switch (std.RCLocation)
                //{
                //    case RectangularCoordinateLocation.Quadrant1:
                //        angle = (Math.Acos(RealAxisHalfLength / std.X) + Math.Atan(std.Y / ImaginaryAxisHalfLength)) / 2;
                //        break;
                //    case RectangularCoordinateLocation.Quadrant2:
                //        angle = Math.Acos(RealAxisHalfLength / std.X);
                //        break;
                //    case RectangularCoordinateLocation.Quadrant3:
                //        angle = 1.5 * Math.PI - (Math.Acos(RealAxisHalfLength / std.X) + Math.Atan(std.Y / ImaginaryAxisHalfLength)) / 2;
                //        break;
                //    case RectangularCoordinateLocation.Quadrant4:
                //        angle = Math.Atan(std.Y / ImaginaryAxisHalfLength);
                //        break;
                //    case RectangularCoordinateLocation.NegativeX:
                //        angle = Math.PI;
                //        break;
                //}
                ControlledOutPointsOriginalInfo.Add(item, new double[] { RealAxisHalfLength / std.X, std.Y / ImaginaryAxisHalfLength });
            }
            base.InternalPrepare(movePoint, sender);
        }
        public override PointD GetOutFreePointRecommendNewLocation(PointBase pt, bool getXY = true)
        {
            PointD xy = NewCenter + new PointD(NewRealAxisHalfLength / ControlledOutPointsOriginalInfo[pt][0], NewImaginaryAxisHalfLength * ControlledOutPointsOriginalInfo[pt][1]).Rotate(NewFocusAngle);
            return getXY ? xy : Pad.XYToPxy(xy);
        }
        public override void InternalClean(PointBase movedPoint, ShapeBase sender = null)
        {
            ControlledOutPointsOriginalInfo.Clear();
            base.InternalClean(movedPoint, sender);
        }
        public override void PaintVirtualObjects(Graphics gra)
        {
            if (CanDrawVirtual)
            {
                foreach (Curve item in pske.Curves)
                {
                    Pen p = Core.Helper.Painter.VirtualLinePen;
                    p.DashStyle = DashStyle.Solid;
                    item.Paint(gra, p);
                }
            }
            base.PaintVirtualObjects(gra);
        }
        void build(Curve curL, Curve pCurL, Curve curR, Curve pCurR, double a, double b, double focusAngle, PointD center)
        {
            if (curL == null && pCurL == null && curR == null && pCurR == null) return;
            curL?.Clear();
            pCurL?.Clear();
            curR?.Clear();
            pCurR?.Clear();
            //cur1.TangentVectors.Clear();
            for (double theta = Core.Helper.Angle.RightAngle; theta < Math.PI * 1.5; theta += step)
            {
                if (theta == Core.Helper.Angle.RightAngle) continue;
                //添加采样点
                //double cos = Math.Cos(theta), rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt = new PointD(rou * cos, rou * Math.Sin(theta)).Rotate(focusAngle) + center;
                PointD pt = new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(focusAngle) + center;
                curL?.Add(pt);
                if (pCurL != null)
                {
                    if (Pad.Coordinate != null) pt = Pad.Coordinate.XYToPxy(pt);
                    if (pt.IsPaintable) pCurL.Add(pt);
                }
                //添加该点处切线方向向量
                //cos = Math.Cos(theta - Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //pt = new PointD(rou * cos, rou * Math.Sin(theta - Curve.DeltaX)).Rotate(focusAngle);
                //cos = Math.Cos(theta + Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt2 = new PointD(rou * cos, rou * Math.Sin(theta + Curve.DeltaX)).Rotate(focusAngle);
                //if (Pad.Coordinate != null)
                //{
                //    pt = Pad.Coordinate.XYToPxy(pt);
                //    pt2 = Pad.Coordinate.XYToPxy(pt2);
                //}
                //cur1.TangentVectors.Add(pt2 - pt);
            }
            //cur2.Samples.Clear();
            //cur2.TangentVectors.Clear();
            for (double theta = Math.PI * 1.5; theta < Core.Helper.Angle.RadRound; theta += step)
            {
                if (theta == Math.PI * 1.5) continue;
                //添加采样点
                //double cos = Math.Cos(theta), rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt = new PointD(rou * cos, rou * Math.Sin(theta)).Rotate(focusAngle) + center;
                PointD pt = new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(focusAngle) + center;
                curR?.Add(pt);
                if (pCurR != null)
                {
                    if (Pad.Coordinate != null) pt = Pad.Coordinate.XYToPxy(pt);
                    if (pt.IsPaintable) pCurR.Add(pt);
                }
                //添加该点处切线方向向量
                //cos = Math.Cos(theta - Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //pt = new PointD(rou * cos, rou * Math.Sin(theta - Curve.DeltaX)).Rotate(focusAngle);
                //cos = Math.Cos(theta + Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt2 = new PointD(rou * cos, rou * Math.Sin(theta + Curve.DeltaX)).Rotate(focusAngle);
                //if (Pad.Coordinate != null)
                //{
                //    pt = Pad.Coordinate.XYToPxy(pt);
                //    pt2 = Pad.Coordinate.XYToPxy(pt2);
                //}
                //cur2.TangentVectors.Add(pt2 - pt);
            }
            for (double theta = 0; theta < Core.Helper.Angle.RightAngle; theta += step)
            {
                //添加采样点
                //double cos = Math.Cos(theta), rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt = new PointD(rou * cos, rou * Math.Sin(theta)).Rotate(focusAngle) + center;
                PointD pt = new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(focusAngle) + center;
                curR?.Add(pt);
                if (pCurR != null)
                {
                    if (Pad.Coordinate != null) pt = Pad.Coordinate.XYToPxy(pt);
                    if (pt.IsPaintable) pCurR.Add(pt);
                }
                //添加该点处切线方向向量
                //cos = Math.Cos(theta - Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //pt = new PointD(rou * cos, rou * Math.Sin(theta - Curve.DeltaX)).Rotate(focusAngle);
                //cos = Math.Cos(theta + Curve.DeltaX);
                //rou = Math.Sqrt(a2 * (a2 - c2) / (a2 - c2 * cos * cos));
                //PointD pt2 = new PointD(rou * cos, rou * Math.Sin(theta + Curve.DeltaX)).Rotate(focusAngle);
                //if (Pad.Coordinate != null)
                //{
                //    pt = Pad.Coordinate.XYToPxy(pt);
                //    pt2 = Pad.Coordinate.XYToPxy(pt2);
                //}
                //cur2.TangentVectors.Add(pt2 - pt);
            }
        }
        public void BuildSamples()
        {
            build(ske.Curves[0], pske.Curves[0], ske.Curves[1], pske.Curves[1], RealAxisHalfLength, ImaginaryAxisHalfLength, FocusAngle, Center);
        }
        protected override string getFormula()
        {
            if (Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
            {
                return string.Empty;//不会求极坐标方程，暂时留空
            }
            else
            {
                return $"[{Math.Sin(FocusAngle)}(y-{Center.Y})+{Math.Cos(FocusAngle)}(x-{Center.X})]^2/{RealAxisHalfLength * RealAxisHalfLength}-[{Math.Cos(FocusAngle)}(y-{Center.Y})-{Math.Sin(FocusAngle)}(x-{Center.X})]^2/{ImaginaryAxisHalfLength * ImaginaryAxisHalfLength}=1";
            }
        }
        //public bool FixOnHyperbola(PointD location, out PointD result)
        //{
        //    double angle = Math.Atan2(b, a), theta = Core.Helper.Angle.RadRound - PCenter.GetAngle(location);
        //    if (theta > Math.PI - angle && theta < Math.PI + angle || theta > Core.Helper.Angle.RadRound - angle && theta < Core.Helper.Angle.RadRound ||
        //        theta >= 0 && theta < angle)
        //    {
        //        theta -= RotateAngle;
        //        double c = 半焦距长, a2 = a * a, c2 = c * c,
        //        cos = Math.Cos(theta), rou = Math.Sqrt((a2 * a2 - a2 * c2) / (a2 - c2 * cos * cos));
        //        result = new PointD(rou * cos, rou * Math.Sin(theta)).Rotate(RotateAngle) + Center;
        //        if (Pad.Coordinate != null) result = Pad.Coordinate.XYToPxy(result);
        //        return true;
        //    }
        //    result = PointD.Empty;
        //    return false;
        //}
        //protected override PointD GetAbsorbedPoint(PointD PLocation, bool getNew = false)
        //{
        //    return PLocation.GetNearestPointFromCurves(getNew ? NewCurves : Curves);
        //}
        //public override bool OnObject(PointD pt)
        //{
        //    return Math.Abs(pt.DistanceTo(Focus1) - pt.DistanceTo(Focus2)).AlmostEqual(2 * RealAxisHalfLength);
        //}
        public override PropertyDisplayer[] PropertyDisplayersCheckNearBy(PointD Plocation)
        {
            List<PropertyDisplayer> res = new List<PropertyDisplayer>(base.PropertyDisplayersCheckNearBy(Plocation));
            if (FocalHalfLengthDisplayer.InRegion(Plocation))
            {
                res.Add(FocalHalfLengthDisplayer);
            }
            if (RealAxisHalfLengthDisplayer.InRegion(Plocation))
            {
                res.Add(RealAxisHalfLengthDisplayer);
            }
            if (ImaginaryAxisHalfLengthDisplayer.InRegion(Plocation))
            {
                res.Add(ImaginaryAxisHalfLengthDisplayer);
            }
            if (FocusPoint1Displayer.InRegion(Plocation))
            {
                res.Add(FocusPoint1Displayer);
            }
            if (FocusPoint2Displayer.InRegion(Plocation))
            {
                res.Add(FocusPoint2Displayer);
            }
            if (EccentricityDisplayer.InRegion(Plocation))
            {
                res.Add(EccentricityDisplayer);
            }
            if (CenterDisplayer.InRegion(Plocation))
            {
                res.Add(CenterDisplayer);
            }
            return res.ToArray();
        }
        public PointD[] GetTangentPoints(PointD passPoint)
        {
            return CurveTangent.GetHyperbolaTangentPoints(NewCenter, NewRealAxisHalfLength, NewImaginaryAxisHalfLength, NewFocusAngle, passPoint);
        }
        public double? GetTangentLineSlope(PointD pt)
        {
            //if (!OnLine(pt))
            //{
            //    if (Pad.Coordinate != null) pt = Pad.Coordinate.XYToPxy(pt);
            //    pt = pt.GetNearestPointFromCurves(NewCurves);
            //    if (Pad.Coordinate != null) pt = Pad.Coordinate.PxyToXY(pt);
            //}
            return Math.Tan((pt.GetAngle(NewFocus1) + pt.GetAngle(NewFocus2)) / 2);
        }
        public override void UseFormat(Base obj)
        {
            base.UseFormat(obj);
            if (Settings.Instance.PropertyDisplayerUseFormat)
            {
                ParabolaBase pb = obj as ParabolaBase;
                if (pb != null)
                {
                    FocusPoint1Displayer.UseFormat(pb.FocusPointDisplayer);
                    FocusPoint2Displayer.UseFormat(pb.FocusPointDisplayer);
                }
            }
        }
    }
}
